package com.fizz.redpacket.service;

import com.fizz.redpacket.algorithm.AllocateStrategy;
/*     */ import com.fizz.redpacket.algorithm.AllocateStrategyFactory;
/*     */ import com.fizz.redpacket.entities.UserGetRecord;
/*     */ import com.fizz.redpacket.entities.UserSendRecord;
/*     */ import com.fizz.redpacket.error.NoSuchRedPacket;
/*     */ import com.fizz.redpacket.lock.MyRedisLock;
/*     */ import com.fizz.redpacket.mapper.primary.UserGetRedPacketMapper;
/*     */ import com.fizz.redpacket.mapper.primary.UserSendRecordMapper;
/*     */ import com.fizz.redpacket.mapper.secondary.SecondarySendPacketMapper;
/*     */ import com.fizz.redpacket.mapper.secondary.SecondaryUserGetRedPacketMapper;
/*     */ import com.fizz.redpacket.redis.RedisRedPacketMapper;
/*     */
/*     */ import com.fizz.redpacket.util.RedPacketUtil;
/*     */ import com.fizz.redpacket.util.RedisUtil;
/*     */ import java.util.Date;
/*     */ import java.util.List;
/*     */ import lombok.extern.slf4j.Slf4j;
/*     */
/*     */ import org.springframework.beans.factory.annotation.Autowired;
/*     */ import org.springframework.scheduling.annotation.Async;
/*     */ import org.springframework.stereotype.Service;

@Slf4j
@Service("RedPacketServiceImpl")
public class RedPacketServiceImpl implements RedPacketService {

    @Autowired
    UserSendRecordMapper userSendRecordMapper;

    @Autowired
    RedisRedPacketMapper redisRedPacketMapper;

    @Autowired
    UserGetRedPacketMapper userGetRedPacketMapper;

    @Autowired
    SecondaryUserGetRedPacketMapper secondaryUserGetRedPacketMapper;

    @Autowired
    SecondarySendPacketMapper secondarySendPacketMapper;

    @Autowired
    MyRedisLock redisLock;

    public String sendPacket(String userId, String amount, int redPacketCount) {
        if (userId == null || amount == null) {
            throw new IllegalArgumentException();
         }
        AllocateStrategy strategy = AllocateStrategyFactory.getStrategy();
        List<String> redPacks = strategy.allocate(redPacketCount, amount);
        String redPacketUUID = RedPacketUtil.generateRedPacketUUID();

        UserSendRecord userSendRecord = new UserSendRecord(userId, redPacketUUID, Double.parseDouble(amount), redPacketCount, new Date());
        writeRedPacket(userSendRecord);
        String redPacketAmountKey = RedisUtil.getRedPacketAmountKey(userId, redPacketUUID);
        String redPacketQueueKey = RedisUtil.getRedPacketQueueKey(userId, redPacketUUID);

        this.redisRedPacketMapper.setValue(redPacketAmountKey, redPacketCount);
        this.redisRedPacketMapper.pushAllQueue(redPacketQueueKey, redPacks.<String>toArray(new String[0]));
        String currentValidRedPacketKey = RedisUtil.getCurrentValidRedPacketKey();
        this.redisRedPacketMapper.addSetExpire(currentValidRedPacketKey, redPacketUUID, 24L);
        return redPacketUUID;
   }

   public UserGetRecord grabRedPacket(String userId, String redPackerId) throws NoSuchRedPacket {
        String currentValidRedPacketKey = RedisUtil.getCurrentValidRedPacketKey();
        boolean isSendRedPacket = this.redisRedPacketMapper.isSetContainsValue(currentValidRedPacketKey, redPackerId);

        if (!isSendRedPacket) {
           log.error("redPacketId不存在");
           throw new NoSuchRedPacket("参数错误, 用户未发过这样的红包. redPacketId" + redPackerId);
         }

        // 相同用户只允许一个抢到红包，此处尝试加锁
        String grabRedPacketUserKey = RedisUtil.getGrabRedPacketUserId(redPackerId);
        if (!this.redisLock.tryLock(userId, redPackerId)) {
           log.info("user{}已经抢过红包", userId);
           return new UserGetRecord(userId, redPackerId, 0.0D, new Date());
         }

        try {
            boolean isGrabRedPacket = this.redisRedPacketMapper.setContains(grabRedPacketUserKey, userId);
            if (isGrabRedPacket) {
                log.info("{}用户重复抢红包", userId);
                return new UserGetRecord(userId, redPackerId, 0.0D, new Date());
            }

            String redPacketAmountKey = RedisUtil.getRedPacketAmountKey(null, redPackerId);
            int redPacketAmount = (Integer) this.redisRedPacketMapper.getValue(redPacketAmountKey);
            if (redPacketAmount <= 0) {
                log.info("用户{},红包余额为0", userId);
                return new UserGetRecord(userId, redPackerId, 0.0D, new Date());
            }

            String redPacketQueueKey = RedisUtil.getRedPacketQueueKey(userId, redPackerId);
            String money = (String)this.redisRedPacketMapper.popQueue(redPacketQueueKey);

            if (money == null) {
                return new UserGetRecord(userId, redPackerId, 0.0D, new Date());
            }

            Double grabMoney = Double.parseDouble(money);
            this.redisRedPacketMapper.decrementValue(redPacketAmountKey);
            UserGetRecord userGetRecord = new UserGetRecord(userId, redPackerId, grabMoney, new Date());
            this.redisRedPacketMapper.addSet(grabRedPacketUserKey, userId);
            log.info("用户{}抢到红包{}", userId, userGetRecord.getMoney());

            // 异步写库
            writeUserGetRedPacket(userGetRecord);
            return userGetRecord;
        } finally {
            this.redisLock.unlock(userId, redPackerId);
        }
    }

    @Async
    public void writeUserGetRedPacket(UserGetRecord userGetRecord) {this.userGetRedPacketMapper.insertUserGetRedPacket(userGetRecord);}

    @Async
    public void writeRedPacket(UserSendRecord userSendRecord) {
        this.userSendRecordMapper.insertUserSendRecord(userSendRecord);
    }

    @Override
    public List<UserSendRecord> getUserSendRecord(String userId, int limit, int offset) {
        return this.secondarySendPacketMapper.getUserSendRecordById(userId, limit, offset);
    }

    @Override
    public List<UserGetRecord> getUserGetRecord(String userId, int limit, int offset) {
        return this.secondaryUserGetRedPacketMapper.getUserRedPacketById(userId, limit, offset);
    }

    @Override
    public boolean addAccount(String userId, String money) {
        //todo
        return false;
    }
}
